/*
GanttProject is an opensource project management tool. License: GPL3
Copyright (C) 2004-2011 Thomas Alexandre, GanttProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package net.sourceforge.ganttproject.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.List;

import net.sourceforge.ganttproject.CustomProperty;
import net.sourceforge.ganttproject.CustomPropertyDefinition;
import net.sourceforge.ganttproject.GanttTask;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.resource.HumanResource;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.roles.Role;
import net.sourceforge.ganttproject.task.CustomColumnsValues;
import net.sourceforge.ganttproject.task.ResourceAssignment;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.util.StringUtils;

/**
 * Class to export the project in csv text format
 * 
 * @author athomas
 */
public class GanttCSVExport {
  private final IGanttProject myProject;

  private final CSVOptions csvOptions;

  private final HumanResourceManager myHrManager;

  private final Task[] myTasks;

  private List<HumanResource> resources;

  private int iMaxSize = 0;

  /** Create a table like output with fixed column widths */
  private boolean bFixedSize;

  public GanttCSVExport(IGanttProject project, CSVOptions csvOptions) {
    myProject = project;
    myTasks = project.getTaskManager().getTasks();
    myHrManager = project.getHumanResourceManager();
    this.csvOptions = csvOptions;
    bFixedSize = csvOptions.bFixedSize;
  }

  /**
   * Save the project as CSV on a stream
   * 
   * @throws IOException
   */
  public void save(OutputStream stream) throws IOException {
    OutputStreamWriter out = new OutputStreamWriter(stream);
    beginToSave(out);
    out.close();
  }

  /** Start saving the csv document. */
  private void beginToSave(OutputStreamWriter out) throws IOException {
    resources = myHrManager.getResources();

    if (csvOptions.bFixedSize) {
      getMaxSize();
    }

    writeTasks(out);
    out.write("\n");

    if (resources.size() > 0) {
      out.write("\n\n");
      writeResources(out);
      out.write("\n");
    }
  }

  private void writeCell(OutputStreamWriter out, String cellValue) throws IOException {
    out.write(correctField(cellValue) + (bFixedSize ? "" : csvOptions.sSeparatedChar));
  }

  private void writeTextCell(OutputStreamWriter out, String cellValue) throws IOException {
    out.write((bFixedSize ? "" : csvOptions.sSeparatedTextChar) + correctField(cellValue)
        + (bFixedSize ? "" : csvOptions.sSeparatedTextChar + csvOptions.sSeparatedChar));
  }

  private void writeTaskHeaders(OutputStreamWriter out) throws IOException {
    if (csvOptions.bExportTaskID) {
      writeCell(out, i18n("tableColID"));
    }
    if (csvOptions.bExportTaskName) {
      writeCell(out, i18n("tableColName"));
    }
    if (csvOptions.bExportTaskStartDate) {
      writeCell(out, i18n("tableColBegDate"));
    }
    if (csvOptions.bExportTaskEndDate) {
      writeCell(out, i18n("tableColEndDate"));
    }
    if (csvOptions.bExportTaskDuration) {
      writeCell(out, i18n("tableColDuration"));
    }
    if (csvOptions.bExportTaskPercent) {
      writeCell(out, i18n("tableColCompletion"));
    }
    if (csvOptions.bExportTaskWebLink) {
      writeCell(out, i18n("webLink"));
    }
    if (csvOptions.bExportTaskResources) {
      writeCell(out, i18n("resources"));
    }
    if (csvOptions.bExportTaskNotes) {
      writeCell(out, i18n("notes"));
    }
    for (CustomPropertyDefinition def : myProject.getTaskCustomColumnManager().getDefinitions()) {
      writeCell(out, def.getName());
    }
    out.write("\n\n");
  }

  private String i18n(String key) {
    return GanttLanguage.getInstance().getText(key);
  }

  /** Write all tasks. */
  private void writeTasks(OutputStreamWriter out) throws IOException {
    writeTaskHeaders(out);
    List<CustomPropertyDefinition> customFields = myProject.getTaskCustomColumnManager().getDefinitions();
    for (int i = 0; i < myTasks.length; i++) {
      Task task = myTasks[i];
      // ID
      if (csvOptions.bExportTaskID) {
        writeCell(out, String.valueOf(task.getTaskID()));
      }
      // Name
      if (csvOptions.bExportTaskName) {
        writeTextCell(out, getName(task));
      }
      // Start Date
      if (csvOptions.bExportTaskStartDate) {
        writeCell(out, task.getStart().toString());
      }
      // End Date
      if (csvOptions.bExportTaskEndDate) {
        writeCell(out, task.getEnd().toString());
      }
      // Duration
      if (csvOptions.bExportTaskDuration) {
        writeCell(out, "" + task.getDuration().getLength());
      }
      // Percent complete
      if (csvOptions.bExportTaskPercent) {
        writeCell(out, "" + task.getCompletionPercentage());
      }
      // Web Link
      if (csvOptions.bExportTaskWebLink) {
        writeTextCell(out, getWebLink((GanttTask) task));
      }
      // associated resources
      if (csvOptions.bExportTaskResources) {
        writeTextCell(out, getAssignments(task));
      }
      // Notes
      if (csvOptions.bExportTaskNotes) {
        writeTextCell(out, task.getNotes());
      }
      CustomColumnsValues customValues = task.getCustomValues();
      for (int j = 0; j < customFields.size(); j++) {
        Object nextCustomFieldValue = customValues.getValue(customFields.get(j));
        writeCell(out, String.valueOf(nextCustomFieldValue));
      }
      out.write("\n");
    }
  }

  private void writeResourceHeaders(OutputStreamWriter out) throws IOException {
    if (csvOptions.bExportResourceID) {
      writeCell(out, i18n("tableColID"));
    }
    if (csvOptions.bExportResourceName) {
      writeCell(out, i18n("tableColResourceName"));
    }
    if (csvOptions.bExportResourceMail) {
      writeCell(out, i18n("tableColResourceEMail"));
    }
    if (csvOptions.bExportResourcePhone) {
      writeCell(out, i18n("tableColResourcePhone"));
    }
    if (csvOptions.bExportResourceRole) {
      writeCell(out, i18n("tableColResourceRole"));
    }
    List<CustomPropertyDefinition> customFieldDefs = myProject.getResourceCustomPropertyManager().getDefinitions();
    for (int i = 0; i < customFieldDefs.size(); i++) {
      CustomPropertyDefinition nextDef = customFieldDefs.get(i);
      writeCell(out, nextDef.getName());
    }
    out.write("\n\n");
  }

  /** write the resources. */
  private void writeResources(OutputStreamWriter out) throws IOException {
    writeResourceHeaders(out);
    // parse all resources
    for (int i = 0; i < resources.size(); i++) {
      HumanResource p = resources.get(i);

      // ID
      if (csvOptions.bExportResourceID) {
        writeCell(out, "" + p.getId());
      }
      // Name
      if (csvOptions.bExportResourceName) {
        writeTextCell(out, p.getName());
      }
      // Mail
      if (csvOptions.bExportResourceMail) {
        writeTextCell(out, p.getMail());
      }
      // Phone
      if (csvOptions.bExportResourcePhone) {
        writeTextCell(out, p.getPhone());
      }
      // Role
      if (csvOptions.bExportResourceRole) {
        Role role = p.getRole();
        String sRoleID = role == null ? "0" : role.getPersistentID();
        writeTextCell(out, sRoleID);
      }
      List<CustomProperty> customProps = p.getCustomProperties();
      for (int j = 0; j < customProps.size(); j++) {
        CustomProperty nextProperty = customProps.get(j);
        writeTextCell(out, nextProperty.getValueAsString());
      }
      out.write("\n");
    }
  }

  /** set the maximum size for all strings. */
  private void getMaxSize() {
    List<CustomPropertyDefinition> customFields = myProject.getTaskCustomColumnManager().getDefinitions();
    iMaxSize = 0;
    for (int i = 0; i < myTasks.length; i++) {
      Task task = myTasks[i];

      if (csvOptions.bExportTaskID) {
        String s = String.valueOf(task.getTaskID());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskName) {
        String s = getName(task);
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskStartDate) {
        String s = String.valueOf(task.getStart());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskEndDate) {
        String s = String.valueOf(task.getEnd());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskDuration) {
        String s = String.valueOf(task.getDuration().getLength());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskPercent) {
        String s = String.valueOf(task.getCompletionPercentage());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskWebLink) {
        String s = getWebLink((GanttTask) task);
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskResources) {
        String s = getAssignments(task);
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      if (csvOptions.bExportTaskNotes) {
        String s = task.getNotes();
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }

      CustomColumnsValues customValues = task.getCustomValues();
      for (int j = 0; j < customFields.size(); j++) {
        Object nextCustomFieldValue = customValues.getValue(customFields.get(j));
        String nextValueAsString = String.valueOf(nextCustomFieldValue);
        if (nextValueAsString.length() > iMaxSize) {
          iMaxSize = nextValueAsString.length();
        }
      }
    }

    // parse all resources
    for (int i = 0; i < resources.size(); i++) {
      HumanResource p = resources.get(i);

      if (csvOptions.bExportResourceID) {
        String s = String.valueOf(p.getId());
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }
      if (csvOptions.bExportResourceName) {
        String s = p.getName();
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }
      if (csvOptions.bExportResourceMail) {
        String s = p.getMail();
        if (s.length() > iMaxSize)
          iMaxSize = s.length();
      }
      if (csvOptions.bExportResourcePhone) {
        String s = p.getPhone();
        if (s.length() > iMaxSize) {
          iMaxSize = s.length();
        }
      }
      if (csvOptions.bExportResourceRole) {
        Role role = p.getRole();
        String sRoleID;
        if (role != null) {
          sRoleID = role.getPersistentID();
        } else {
          sRoleID = "0";
        }
        if (sRoleID.length() > iMaxSize) {
          iMaxSize = sRoleID.length();
        }
      }
      List<CustomProperty> customProps = p.getCustomProperties();
      for (int j = 0; j < customProps.size(); j++) {
        CustomProperty nextProperty = customProps.get(j);
        if (nextProperty.getValueAsString().length() > iMaxSize) {
          iMaxSize = nextProperty.getValueAsString().length();
        }
      }
    }
  }

  /** @return the name of task with the correct level. */
  private String getName(Task task) {
    if (bFixedSize) {
      return task.getName();
    }
    int depth = task.getManager().getTaskHierarchy().getDepth(task);
    return StringUtils.padLeft(task.getName(), depth * 2);
  }

  /** @return the link of the task. */
  private String getWebLink(GanttTask task) {
    return (task.getWebLink().equals("http://") ? "" : task.getWebLink());
  }

  /** @return the list of the assignment for the resources. */
  private String getAssignments(Task task) {
    String res = "";
    ResourceAssignment[] assignment = task.getAssignments();
    for (int i = 0; i < assignment.length; i++) {
      res += (assignment[i].getResource() + (i == assignment.length - 1 ? ""
          : csvOptions.sSeparatedChar.equals(";") ? "," : ";"));
    }
    return res;
  }

  private String correctField(String field) {
    return StringUtils.padLeft(field, iMaxSize - field.length());
  }
}
